//+-------------------------------------------------------------------------
//
//  Copyright (c) Microsoft Corporation.  All rights reserved.
//
//  File:       utils.cpp
//
//--------------------------------------------------------------------------

#include "setup.h"
#include "resource.h"
#include "common.h"

#include "msi.h"

#include <assert.h>
#include <tchar.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <strsafe.h>
#include "WinBase.h"

#define WIN // scope W32 API
#define CountOf( array ) ( sizeof( array )/sizeof( array[0] ) )
// Constants that represent registry key names and value names
// to use for detection
const TCHAR *g_szNetfx10RegKeyName = _T("Software\\Microsoft\\.NETFramework\\Policy\\v1.0");
const TCHAR *g_szNetfx10RegKeyValue = _T("3705");
const TCHAR *g_szNetfx10SPxMSIRegKeyName = _T("Software\\Microsoft\\Active Setup\\Installed Components\\{78705f0d-e8db-4b2d-8193-982bdda15ecd}");
const TCHAR *g_szNetfx10SPxOCMRegKeyName = _T("Software\\Microsoft\\Active Setup\\Installed Components\\{FDC11A6F-17D1-48f9-9EA3-9051954BAA24}");
const TCHAR *g_szNetfx11RegKeyName = _T("Software\\Microsoft\\NET Framework Setup\\NDP\\v1.1.4322");
const TCHAR *g_szNetfx20RegKeyName = _T("Software\\Microsoft\\NET Framework Setup\\NDP\\v2.0.50727");
const TCHAR *g_szNetfx30RegKeyName = _T("Software\\Microsoft\\NET Framework Setup\\NDP\\v3.0\\Setup");
const TCHAR *g_szNetfx30SpRegKeyName = _T("Software\\Microsoft\\NET Framework Setup\\NDP\\v3.0");
const TCHAR *g_szNetfx30RegValueName = _T("InstallSuccess");
const TCHAR *g_szNetfx35RegKeyName = _T("Software\\Microsoft\\NET Framework Setup\\NDP\\v3.5");
const TCHAR *g_szNetfx40ClientRegKeyName = _T("Software\\Microsoft\\NET Framework Setup\\NDP\\v4\\Client");
const TCHAR *g_szNetfx40FullRegKeyName = _T("Software\\Microsoft\\NET Framework Setup\\NDP\\v4\\Full");
const TCHAR *g_szNetfx40SPxRegValueName = _T("Servicing");
const TCHAR *g_szNetfxStandardRegValueName = _T("Install");
const TCHAR *g_szNetfxInstallPathRegValueName = _T("InstallPath");
const TCHAR *g_szNetfxStandardSPxRegValueName = _T("SP");
const TCHAR *g_szNetfxStandardVersionRegValueName = _T("Version");

// Version information for final release of .NET Framework 3.0
const int g_iNetfx30VersionMajor = 3;
const int g_iNetfx30VersionMinor = 0;
const int g_iNetfx30VersionBuild = 4506;
const int g_iNetfx30VersionRevision = 26;

// Version information for final release of .NET Framework 3.5
const int g_iNetfx35VersionMajor = 3;
const int g_iNetfx35VersionMinor = 5;
const int g_iNetfx35VersionBuild = 21022;
const int g_iNetfx35VersionRevision = 8;

// Version information for final release of .NET Framework 4
const int g_iNetfx40VersionMajor = 4;
const int g_iNetfx40VersionMinor = 0;
const int g_iNetfx40VersionBuild = 30319;
const int g_iNetfx40VersionRevision = 0;

// Constants for known .NET Framework versions used with the GetRequestedRuntimeInfo API
const TCHAR *g_szNetfx10VersionString = _T("v1.0.3705");
const TCHAR *g_szNetfx11VersionString = _T("v1.1.4322");
const TCHAR *g_szNetfx20VersionString = _T("v2.0.50727");
const TCHAR *g_szNetfx40VersionString = _T("v4.0.30319");

// Function prototypes
bool CheckNetfxBuildNumber(const TCHAR*, const TCHAR*, const int, const int, const int, const int);
bool CheckNetfxVersionUsingMscoree(const TCHAR*);
bool IsNetFx40Installed();
DWORD GetProcessorArchitectureFlag();
bool IsNetfx40ClientInstalled();
bool IsNetfx40FullInstalled();
bool RegistryGetValue(HKEY, const TCHAR*, const TCHAR*, DWORD, LPBYTE, DWORD);

typedef enum {

    RUNTIME_INFO_UPGRADE_VERSION         = 0x01,
    RUNTIME_INFO_REQUEST_IA64            = 0x02,
    RUNTIME_INFO_REQUEST_AMD64           = 0x04,
    RUNTIME_INFO_REQUEST_X86             = 0x08,
    RUNTIME_INFO_DONT_RETURN_DIRECTORY   = 0x10,
    RUNTIME_INFO_DONT_RETURN_VERSION     = 0x20,
    RUNTIME_INFO_DONT_SHOW_ERROR_DIALOG  = 0x40

} RUNTIME_INFO_FLAGS;
typedef enum {
    STARTUP_CONCURRENT_GC                         = 0x1,
    STARTUP_LOADER_OPTIMIZATION_MASK              = 0x3<<1,
    STARTUP_LOADER_OPTIMIZATION_SINGLE_DOMAIN     = 0x1<<1,
    STARTUP_LOADER_OPTIMIZATION_MULTI_DOMAIN      = 0x2<<1,
    STARTUP_LOADER_OPTIMIZATION_MULTI_DOMAIN_HOST = 0x3<<1,

    STARTUP_LOADER_SAFEMODE                       = 0x10,
    STARTUP_LOADER_SETPREFERENCE                  = 0x100,

    STARTUP_SERVER_GC                             = 0x1000,
    STARTUP_HOARD_GC_VM                           = 0x2000,

    STARTUP_SINGLE_VERSION_HOSTING_INTERFACE      = 0x4000,
    STARTUP_LEGACY_IMPERSONATION                  = 0x10000,
    STARTUP_DISABLE_COMMITTHREADSTACK             = 0x20000,
    STARTUP_ALWAYSFLOW_IMPERSONATION              = 0x40000,
    STARTUP_TRIM_GC_COMMIT                        = 0x80000,

    STARTUP_ETW                                   = 0x100000,
    STARTUP_ARM                                   = 0x400000
} STARTUP_FLAGS;

/******************************************************************
Function Name:  CheckNetfxVersionUsingMscoree
Description:    Uses the logic described in the sample code at
                http://msdn2.microsoft.com/library/ydh6b3yb.aspx
                to load mscoree.dll and call its APIs to determine
                whether or not a specific version of the .NET
                Framework is installed on the system
Inputs:         pszNetfxVersionToCheck - version to look for
Results:        true if the requested version is installed
                false otherwise
******************************************************************/
bool CheckNetfxVersionUsingMscoree(const TCHAR *pszNetfxVersionToCheck)
{
	bool bFoundRequestedNetfxVersion = false;
	HRESULT hr = S_OK;

	// Check input parameter
	if (NULL == pszNetfxVersionToCheck)
		return false;

	HMODULE hmodMscoree = LoadLibraryEx(_T("mscoree.dll"), NULL, 0);
	if (NULL != hmodMscoree)
	{
		typedef HRESULT (STDAPICALLTYPE *GETCORVERSION)(LPWSTR szBuffer, DWORD cchBuffer, DWORD* dwLength);
		GETCORVERSION pfnGETCORVERSION = (GETCORVERSION)GetProcAddress(hmodMscoree, "GetCORVersion");

		// Some OSs shipped with a placeholder copy of mscoree.dll. The existence of mscoree.dll
		// therefore does NOT mean that a version of the .NET Framework is installed.
		// If this copy of mscoree.dll does not have an exported function named GetCORVersion
		// then we know it is a placeholder DLL.
		if (NULL == pfnGETCORVERSION)
			goto Finish;

		typedef HRESULT (STDAPICALLTYPE *CORBINDTORUNTIME)(LPCWSTR pwszVersion,
			LPCWSTR pwszBuildFlavor, 
			REFCLSID rclsid, 
			REFIID riid, 
			LPVOID FAR *ppv);

		CORBINDTORUNTIME pfnCORBINDTORUNTIME = (CORBINDTORUNTIME)GetProcAddress(hmodMscoree, "CorBindToRuntime");

		typedef HRESULT (STDAPICALLTYPE *GETREQUESTEDRUNTIMEINFO)(LPCWSTR pExe,
			LPCWSTR pwszVersion,
			LPCWSTR pConfigurationFile,
			DWORD startupFlags,
			DWORD runtimeInfoFlags,
			LPWSTR pDirectory,
			DWORD dwDirectory,
			DWORD *dwDirectoryLength,
			LPWSTR pVersion,
			DWORD cchBuffer,
			DWORD* dwlength);
		GETREQUESTEDRUNTIMEINFO pfnGETREQUESTEDRUNTIMEINFO = (GETREQUESTEDRUNTIMEINFO)GetProcAddress(hmodMscoree, "GetRequestedRuntimeInfo");

		if (NULL != pfnCORBINDTORUNTIME)
		{
			TCHAR szRetrievedVersion[MAX_PATH];
			DWORD dwLength = CountOf(szRetrievedVersion);

			if (NULL == pfnGETREQUESTEDRUNTIMEINFO)
			{
				// Having CorBindToRuntimeHost but not having GetRequestedRuntimeInfo means that
				// this machine contains no higher than .NET Framework 1.0, but the only way to
				// 100% guarantee that the .NET Framework 1.0 is installed is to call a function
				// to exercise its functionality
				if (0 == _tcscmp(pszNetfxVersionToCheck, g_szNetfx10VersionString))
				{
					hr = pfnGETCORVERSION((LPWSTR)szRetrievedVersion, dwLength, &dwLength);

					if (SUCCEEDED(hr))
					{
						if (0 == _tcscmp(szRetrievedVersion, g_szNetfx10VersionString))
							bFoundRequestedNetfxVersion = true;
					}

					goto Finish;
				}
			}

			// Set error mode to prevent the .NET Framework from displaying
			// unfriendly error dialogs
			UINT uOldErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS);

			TCHAR szDirectory[MAX_PATH];
			DWORD dwDirectoryLength = 0;
			DWORD dwRuntimeInfoFlags = RUNTIME_INFO_DONT_RETURN_DIRECTORY | GetProcessorArchitectureFlag();

			// Check for the requested .NET Framework version
			hr = pfnGETREQUESTEDRUNTIMEINFO(NULL,
				(LPWSTR)pszNetfxVersionToCheck,
				NULL, 
				STARTUP_LOADER_OPTIMIZATION_MULTI_DOMAIN_HOST, 
				NULL, 
				(LPWSTR)szDirectory, 
				CountOf(szDirectory),
				&dwDirectoryLength, 
				(LPWSTR)szRetrievedVersion, 
				CountOf(szRetrievedVersion), 
				&dwLength);
			if (hr == ERROR_INSUFFICIENT_BUFFER)
			{
				goto Finish;
			}

			if (SUCCEEDED(hr))
				bFoundRequestedNetfxVersion = true;

			// Restore the previous error mode
			SetErrorMode(uOldErrorMode);
		}
	}

Finish:
	if (hmodMscoree)
	{
		FreeLibrary(hmodMscoree);
	}

	return bFoundRequestedNetfxVersion;
}


/******************************************************************
Function Name:  GetProcessorArchitectureFlag
Description:    Determine the processor architecture of the
                system (x86, x64, ia64)
Inputs:         NONE
Results:        DWORD processor architecture flag
******************************************************************/
DWORD GetProcessorArchitectureFlag()
{
	HMODULE hmodKernel32 = NULL;
	typedef void (WINAPI *PFnGetNativeSystemInfo) (LPSYSTEM_INFO);
	PFnGetNativeSystemInfo pfnGetNativeSystemInfo;

	SYSTEM_INFO sSystemInfo;
	memset(&sSystemInfo, 0, sizeof(sSystemInfo));

	bool bRetrievedSystemInfo = false;

	// Attempt to load kernel32.dll
	hmodKernel32 = LoadLibrary(_T("Kernel32.dll"));
	if (NULL != hmodKernel32)
	{
		// If the DLL loaded correctly, get the proc address for GetNativeSystemInfo
		pfnGetNativeSystemInfo = (PFnGetNativeSystemInfo) GetProcAddress(hmodKernel32, "GetNativeSystemInfo");
		if (NULL != pfnGetNativeSystemInfo)
		{
			// Call GetNativeSystemInfo if it exists
			(*pfnGetNativeSystemInfo)(&sSystemInfo);
			bRetrievedSystemInfo = true;
		}
		FreeLibrary(hmodKernel32);
	}

	if (!bRetrievedSystemInfo)
	{
		// Fallback to calling GetSystemInfo if the above failed
		GetSystemInfo(&sSystemInfo);
		bRetrievedSystemInfo = true;
	}

	if (bRetrievedSystemInfo)
	{
		switch (sSystemInfo.wProcessorArchitecture) 
		{
			case PROCESSOR_ARCHITECTURE_INTEL:
				return RUNTIME_INFO_REQUEST_X86;
			case PROCESSOR_ARCHITECTURE_IA64:
				return RUNTIME_INFO_REQUEST_IA64;
			case PROCESSOR_ARCHITECTURE_AMD64:
				return RUNTIME_INFO_REQUEST_AMD64;
			default:
				return 0;
		}
	}

	return 0;
}

/******************************************************************
Function Name:	RegistryGetValue
Description:	Get the value of a reg key
Inputs:			HKEY hk - The hk of the key to retrieve
				TCHAR *pszKey - Name of the key to retrieve
				TCHAR *pszValue - The value that will be retrieved
				DWORD dwType - The type of the value that will be retrieved
				LPBYTE data - A buffer to save the retrieved data
				DWORD dwSize - The size of the data retrieved
Results:		true if successful, false otherwise
******************************************************************/
bool RegistryGetValue(HKEY hk, const TCHAR * pszKey, const TCHAR * pszValue, DWORD dwType, LPBYTE data, DWORD dwSize)
{
	HKEY hkOpened;

	// Try to open the key
	if (RegOpenKeyEx(hk, pszKey, 0, KEY_READ, &hkOpened) != ERROR_SUCCESS)
	{
		return false;
	}

	// If the key was opened, try to retrieve the value
	if (RegQueryValueEx(hkOpened, pszValue, 0, &dwType, (LPBYTE)data, &dwSize) != ERROR_SUCCESS)
	{
		RegCloseKey(hkOpened);
		return false;
	}
	
	// Clean up
	RegCloseKey(hkOpened);

	return true;
}
/******************************************************************
Function Name:	CheckNetfxBuildNumber
Description:	Retrieves the .NET Framework build number from
                the registry and validates that it is not a pre-release
                version number
Inputs:         NONE
Results:        true if the build number in the registry is greater
				than or equal to the passed in version; false otherwise
******************************************************************/
bool CheckNetfxBuildNumber(const TCHAR *pszNetfxRegKeyName, const TCHAR *pszNetfxRegKeyValue, const int iRequestedVersionMajor, const int iRequestedVersionMinor, const int iRequestedVersionBuild, const int iRequestedVersionRevision)
{
	TCHAR szRegValue[MAX_PATH];
	TCHAR *pszToken = NULL;
	TCHAR *pszNextToken = NULL;
	int iVersionPartCounter = 0;
	int iRegistryVersionMajor = 0;
	int iRegistryVersionMinor = 0;
	int iRegistryVersionBuild = 0;
	int iRegistryVersionRevision = 0;
	bool bRegistryRetVal = false;

	// Attempt to retrieve the build number registry value
	bRegistryRetVal = RegistryGetValue(HKEY_LOCAL_MACHINE, pszNetfxRegKeyName, pszNetfxRegKeyValue, NULL, (LPBYTE)szRegValue, MAX_PATH);

	if (bRegistryRetVal)
	{
		// This registry value should be of the format
		// #.#.#####.##.  Try to parse the 4 parts of
		// the version here
		pszToken = _tcstok_s(szRegValue, _T("."), &pszNextToken);
		while (NULL != pszToken)
		{
			iVersionPartCounter++;

			switch (iVersionPartCounter)
			{
			case 1:
				// Convert the major version value to an integer
				iRegistryVersionMajor = _tstoi(pszToken);
				break;
			case 2:
				// Convert the minor version value to an integer
				iRegistryVersionMinor = _tstoi(pszToken);
				break;
			case 3:
				// Convert the build number value to an integer
				iRegistryVersionBuild = _tstoi(pszToken);
				break;
			case 4:
				// Convert the revision number value to an integer
				iRegistryVersionRevision = _tstoi(pszToken);
				break;
			default:
				break;

			}

			// Get the next part of the version number
			pszToken = _tcstok_s(NULL, _T("."), &pszNextToken);
		}
	}

	// Compare the version number retrieved from the registry with
	// the version number of the final release of the .NET Framework
	// that we are checking
	if (iRegistryVersionMajor > iRequestedVersionMajor)
	{
		return true;
	}
	else if (iRegistryVersionMajor == iRequestedVersionMajor)
	{
		if (iRegistryVersionMinor > iRequestedVersionMinor)
		{
			return true;
		}
		else if (iRegistryVersionMinor == iRequestedVersionMinor)
		{
			if (iRegistryVersionBuild > iRequestedVersionBuild)
			{
				return true;
			}
			else if (iRegistryVersionBuild == iRequestedVersionBuild)
			{
				if (iRegistryVersionRevision >= iRequestedVersionRevision)
				{
					return true;
				}
			}
		}
	}

	// If we get here, the version in the registry must be less than the
	// version of the final release of the .NET Framework we are checking,
	// so return false
	return false;
}

bool CheckInstallFOlderExists(const TCHAR *pszNetfxRegKeyName )
{
	TCHAR szRegValue[MAX_PATH];
	TCHAR *pszToken = NULL;
	TCHAR *pszNextToken = NULL;
	int iVersionPartCounter = 0;
	int iRegistryVersionMajor = 0;
	int iRegistryVersionMinor = 0;
	int iRegistryVersionBuild = 0;
	int iRegistryVersionRevision = 0;
	bool bRegistryRetVal = false;

	// Attempt to retrieve the build number registry value
	bRegistryRetVal = RegistryGetValue(HKEY_LOCAL_MACHINE, pszNetfxRegKeyName, g_szNetfxInstallPathRegValueName, NULL, (LPBYTE)szRegValue, MAX_PATH);

	if (bRegistryRetVal)
	{
		return INVALID_FILE_ATTRIBUTES != GetFileAttributes(szRegValue);
	}

	

	// If we get here, the version in the registry must be less than the
	// version of the final release of the .NET Framework we are checking,
	// so return false
	return false;
}

/******************************************************************
Function Name:	IsNetfx40ClientInstalled
Description:	Uses the detection method recommended at
                http://msdn.microsoft.com/library/ee942965(v=VS.100).aspx
                to determine whether the .NET Framework 4 Client is
                installed on the machine
Inputs:         NONE
Results:        true if the .NET Framework 4 Client is installed
                false otherwise
******************************************************************/
bool IsNetfx40ClientInstalled()
{
	bool bRetValue = false;
	DWORD dwRegValue=0;

	if (RegistryGetValue(HKEY_LOCAL_MACHINE, g_szNetfx40ClientRegKeyName, g_szNetfxStandardRegValueName, NULL, (LPBYTE)&dwRegValue, sizeof(DWORD)))
	{
		if (1 == dwRegValue)
			bRetValue = true;
	}
	if (! CheckInstallFOlderExists(g_szNetfx40ClientRegKeyName))
	{
		bRetValue = false;
	}

	// A system with a pre-release version of the .NET Framework 4 can
	// have the Install value.  As an added verification, check the
	// version number listed in the registry
	return (bRetValue 
		&& CheckNetfxBuildNumber(g_szNetfx40ClientRegKeyName, g_szNetfxStandardVersionRegValueName, g_iNetfx40VersionMajor, g_iNetfx40VersionMinor, g_iNetfx40VersionBuild, g_iNetfx40VersionRevision)
		);
}

/******************************************************************
Function Name:	IsNetfx40FullInstalled
Description:	Uses the detection method recommended at
                http://msdn.microsoft.com/library/ee942965(v=VS.100).aspx
                to determine whether the .NET Framework 4 Full is
                installed on the machine
Inputs:         NONE
Results:        true if the .NET Framework 4 Full is installed
                false otherwise
******************************************************************/
bool IsNetfx40FullInstalled()
{
	bool bRetValue = false;
	DWORD dwRegValue=0;

	if (RegistryGetValue(HKEY_LOCAL_MACHINE, g_szNetfx40FullRegKeyName, g_szNetfxStandardRegValueName, NULL, (LPBYTE)&dwRegValue, sizeof(DWORD)))
	{
		if (1 == dwRegValue)
			bRetValue = true;
	}
	if (! CheckInstallFOlderExists(g_szNetfx40ClientRegKeyName))
	{
		bRetValue = false;
	}
	// A system with a pre-release version of the .NET Framework 4 can
	// have the Install value.  As an added verification, check the
	// version number listed in the registry
	return (bRetValue 
		&& CheckNetfxBuildNumber(g_szNetfx40FullRegKeyName, g_szNetfxStandardVersionRegValueName, g_iNetfx40VersionMajor, g_iNetfx40VersionMinor, g_iNetfx40VersionBuild, g_iNetfx40VersionRevision)
		);
}


bool IsNetFx40Installed()
{
	/*bool bNetfx40ClientInstalled = (IsNetfx40ClientInstalled() && CheckNetfxVersionUsingMscoree(g_szNetfx40VersionString));
	bool bNetfx40FullInstalled = (IsNetfx40FullInstalled() && CheckNetfxVersionUsingMscoree(g_szNetfx40VersionString));*/
	bool bNetfx40ClientInstalled = (IsNetfx40ClientInstalled());
	bool bNetfx40FullInstalled = (IsNetfx40FullInstalled() );
	return bNetfx40ClientInstalled || bNetfx40FullInstalled;
}



/////////////////////////////////////////////////////////////////////////////
// VerifyFileSignature
//
DWORD VerifyFileSignature (LPCSTR lpszModule, __in_opt LPSTR lpszCmdLine)
{
    LPCSTR  pszFirstArgEnd;
    LPCSTR  pszFileName;
    LPCSTR  pszEnd;
    DWORD   Status;
    
    //
    // When this function is called, the first argument has already
    // been verified. So skip the first argument.
    //
    GetNextArgument (lpszCmdLine, NULL, &pszFirstArgEnd, NULL);
    
    // Now get the name of the file whose signature needs to be verified.
    Status = GetNextArgument (CharNextA(pszFirstArgEnd), &pszFileName, &pszEnd, NULL);
    
    // Must supply a filename
    if (ERROR_NO_MORE_ITEMS == Status)
        return ERROR_BAD_ARGUMENTS;
    
    // Should not have any more arguments
    if ('\0' != *(CharNextA(pszEnd)) &&
        ERROR_NO_MORE_ITEMS != GetNextArgument (CharNextA(CharNextA(pszEnd)), NULL, NULL, NULL))
    {
        return ERROR_BAD_ARGUMENTS;
    }
    
    // We have the right arguments. Null terminate the filename.
    *(CharNextA(pszEnd)) = '\0';
    
    switch (IsPackageTrusted(lpszModule, pszFileName, NULL))
    {
    case itvWintrustNotOnMachine:
        return TRUST_E_PROVIDER_UNKNOWN;
    case itvTrusted:
        return ERROR_SUCCESS;
    case itvUnTrusted:
    default:
        return TRUST_E_SUBJECT_NOT_TRUSTED;
    }
}

/////////////////////////////////////////////////////////////////////////////
// GetExecutionMode
//
emEnum GetExecutionMode (LPCSTR lpszCmdLine)
{
    LPCSTR  pszStart = NULL;
    LPCSTR  pszEnd = NULL;
    DWORD   dwStatus = ERROR_SUCCESS;
    bool    fQuoted = false;
    //
    // Check the first argument and set the execution mode accordingly.
    // When run without arguments, it is assumed that the default install
    // preset by the package publisher needs to be performed.
    //
    // In case an invalid option is provided, the help dialog describing the
    // usage must be displayed.
    //
    dwStatus = GetNextArgument (lpszCmdLine, &pszStart, &pszEnd, &fQuoted);
    
    if (ERROR_NO_MORE_ITEMS == dwStatus)
        return emPreset;
    
    // The only allowed values in the first argument are /a, /v and /?
    if (pszEnd != CharNextA(pszStart) || fQuoted)
        return emHelp;
    
    if ('/' != (*pszStart) && '-' != (*pszStart))
        return emHelp;
    
    switch (*pszEnd)
    {
    case 'a':
    case 'A':
        return emAdminInstall;
    case 'v':
    case 'V':
        return emVerify;
    default:
        return emHelp;
    }
}

/////////////////////////////////////////////////////////////////////////////
// GetNextArgument
//
DWORD GetNextArgument (LPCSTR pszCmdLine, LPCSTR *ppszArgStart, LPCSTR *ppszArgEnd, bool * pfQuoted)
{
    bool    fInQuotes = false;
    bool    fFoundArgEnd = false;
    LPCSTR  pszChar = pszCmdLine;
    LPCSTR  pszFirst = NULL;
    LPCSTR  pszLast = NULL;
    
    if (NULL == pszChar)
        return ERROR_NO_MORE_ITEMS;
    
    // Skip leading spaces.
    while (' ' == *pszChar || '\t' == *pszChar)
        pszChar = CharNextA(pszChar);
    
    // Check if we have run out of arguments.
    if ('\0' == (*pszChar))
        return ERROR_NO_MORE_ITEMS;
    
    // Check if we this argument has been enclosed in quotes
    if ('\"' == (*pszChar))
    {
        fInQuotes = true;
        pszChar = CharNextA (pszChar);
    }
        
    pszFirst = pszChar;
    
    // Now look for the end of the argument
    while (! fFoundArgEnd)
    {
        pszChar = CharNextA(pszChar);
        
        if ('\0' == (*pszChar))
            fFoundArgEnd = true;
        
        if (fInQuotes && '\"' == (*pszChar))
            fFoundArgEnd = true;
        
        if (!fInQuotes && ' ' == (*pszChar))
            fFoundArgEnd = true;
        
        if (!fInQuotes && '\t' == (*pszChar))
            fFoundArgEnd = true;
    }
    
    pszLast = CharPrevA (pszFirst, pszChar);
    
    if (ppszArgStart)
        *ppszArgStart = pszFirst;
    
    if (ppszArgEnd)
        *ppszArgEnd = pszLast;
    
    if (pfQuoted)
        *pfQuoted = fInQuotes;
    
    return ERROR_SUCCESS;
}

/////////////////////////////////////////////////////////////////////////////
//
//
DWORD GetAdminInstallInfo (bool fPatch, __in_opt LPSTR lpszCmdLine, LPCSTR * ppszAdminImagePath)
{
    LPCSTR  pszFirstArgEnd;
    LPCSTR  pszFileName;
    LPCSTR  pszEnd;
    DWORD   Status;
    
    //
    // When this function is called, the first argument has already been
    // verified. So skip the first argument.
    //
    GetNextArgument (lpszCmdLine, NULL, &pszFirstArgEnd, NULL);
    
    // See if there is another argument
    Status = GetNextArgument (CharNextA(pszFirstArgEnd), &pszFileName, &pszEnd, NULL);
    
    // If it is not a patch, there should not be any more arguments.
    if (!fPatch)
    {
        if (ERROR_NO_MORE_ITEMS != Status)
            return ERROR_BAD_ARGUMENTS;
        
        // If we are here, then we are done, because we have all the information we need.
        if (ppszAdminImagePath)
            *ppszAdminImagePath = NULL;
        return ERROR_SUCCESS;
    }
    
    // If we are here, this is a patch. Get the path to the admin. install.
    if (ERROR_NO_MORE_ITEMS == Status)
        return ERROR_BAD_ARGUMENTS;     // No path was supplied.
    
    // Should not have any more arguments.
    if ('\0' != *(CharNextA(pszEnd)) &&
        ERROR_NO_MORE_ITEMS != GetNextArgument (CharNextA(CharNextA(pszEnd)), NULL, NULL, NULL))
    {
        return ERROR_BAD_ARGUMENTS;
    }
    
    // We have the right arguments. Null terminate the pathname.
    *(CharNextA(pszEnd)) = '\0';
    
    if (ppszAdminImagePath)
        *ppszAdminImagePath = pszFileName;
    
    return ERROR_SUCCESS;
}

/////////////////////////////////////////////////////////////////////////////
// LoadResourceString
//
UINT LoadResourceString(HINSTANCE hInst, LPCSTR lpType, LPCSTR lpName, __out_ecount(*pdwBufSize) LPSTR lpBuf, DWORD *pdwBufSize)
{
    HRSRC   hRsrc   = 0;
    HGLOBAL hGlobal = 0;
    WCHAR   *pch    = 0;

    if ((hRsrc = WIN::FindResource(hInst, lpName, lpType)) != 0
        && (hGlobal = WIN::LoadResource(hInst, hRsrc)) != 0)
    {
        // resource exists
        if ((pch = (WCHAR*)LockResource(hGlobal)) != 0)
        {
            unsigned int cch = WideCharToMultiByte(CP_ACP, 0, pch, -1, NULL, 0, NULL, NULL);
            if (cch > *pdwBufSize)
            {
                *pdwBufSize = cch;
                return ERROR_MORE_DATA;
            }

            if (0 == WideCharToMultiByte(CP_ACP, 0, pch, -1, lpBuf, *pdwBufSize, NULL, NULL))
                return ERROR_FUNCTION_FAILED;
            *pdwBufSize = cch;

        }
        else
        {
            if (1 > *pdwBufSize)
            {
                *pdwBufSize = 1;
                return ERROR_MORE_DATA;
            }

            *pdwBufSize = 1;
            *lpBuf = 0;
        }
        
        DebugMsg("[Resource] lpName = %s, lpBuf = %s\n", lpName, lpBuf);

        return ERROR_SUCCESS;
    }

    // resource does not exist
    DebugMsg("[Resource] lpName = %s NOT FOUND\n", lpName);

    return ERROR_RESOURCE_NAME_NOT_FOUND;
}

/////////////////////////////////////////////////////////////////////////////
// SetupLoadResourceString
//

UINT SetupLoadResourceString(HINSTANCE hInst, LPCSTR lpName, __deref_out LPSTR *lppBuf, DWORD dwBufSize)
{
    UINT uiStat = 0;
    if (!*lppBuf)
    {
        dwBufSize = (dwBufSize > 0) ? dwBufSize : 256;
        *lppBuf = new char[dwBufSize];
        if (!*lppBuf)
            return ERROR_OUTOFMEMORY;
    }

    if (ERROR_SUCCESS != (uiStat = LoadResourceString(hInst, RT_INSTALL_PROPERTY, lpName, *lppBuf, &dwBufSize)))
    {
        if (uiStat != ERROR_MORE_DATA)
            return uiStat;

        // resize and try again
        delete [] *lppBuf;
        *lppBuf = new char[dwBufSize];
        if (!*lppBuf)
            return ERROR_OUTOFMEMORY;

        uiStat = LoadResourceString(hInst, RT_INSTALL_PROPERTY, lpName, *lppBuf, &dwBufSize);
    }

    return uiStat;
}

/////////////////////////////////////////////////////////////////////////////
// PostResourceNotFoundError
//

void PostResourceNotFoundError(HINSTANCE hInst, HWND hwndOwner, LPCSTR szTitle, LPCSTR szName)
{
    char szError[MAX_STR_LENGTH]  = {0};
    char szFormat[MAX_STR_LENGTH] = {0};

    WIN::LoadString(hInst, IDS_MISSING_RESOURCE, szFormat, sizeof(szFormat)/sizeof(char));
    StringCchPrintf(szError, sizeof(szError), szFormat, szName);
    MessageBox(hwndOwner, szError, szTitle, MB_OK | MB_ICONEXCLAMATION);
}

/////////////////////////////////////////////////////////////////////////////
// ReportUserUnSuccessfullFwInstall
//

void ReportUserUnSuccessfullFwInstall(HINSTANCE hInst, HWND hwndOwner, LPCSTR szTitle)
{
    char szError[MAX_STR_LENGTH] = {0};

    WIN::LoadString(hInst, IDS_USER_CANCELLED, szError, sizeof(szError)/sizeof(char));
    MessageBox(hwndOwner, szError, szTitle, MB_OK | MB_ICONEXCLAMATION);
}

/////////////////////////////////////////////////////////////////////////////
// ReportUserCancelled
//

void ReportUserCancelled(HINSTANCE hInst, HWND hwndOwner, LPCSTR szTitle)
{
    char szError[MAX_STR_LENGTH] = {0};

    WIN::LoadString(hInst, IDS_USER_CANCELLED, szError, sizeof(szError)/sizeof(char));
    MessageBox(hwndOwner, szError, szTitle, MB_OK | MB_ICONEXCLAMATION);
}

/////////////////////////////////////////////////////////////////////////////
// PostError
//

void PostError(HINSTANCE hInst, HWND hwndOwner, LPCSTR szTitle, UINT uiErrorId)
{
    char szError[MAX_STR_LENGTH]  = {0};

    WIN::LoadString(hInst, uiErrorId, szError, sizeof(szError)/sizeof(char));
    MessageBox(hwndOwner, szError, szTitle, MB_OK | MB_ICONERROR);
}

bool PromptForNetFWInstall(HINSTANCE hInst, HWND hwndOwner)
{
	//char szError[MAX_STR_LENGTH]  = {0};
 //   char szFormat[MAX_STR_LENGTH] = {0};

 //   WIN::LoadString(hInst, IDS_MISSING_RESOURCE, szFormat, sizeof(szFormat)/sizeof(char));
	//MessageBox(hwndOwner, szError, szTitle, MB_OKCANCEL| MB_ICONINFORMATION);
	return true;
}

/////////////////////////////////////////////////////////////////////////////
// PostError
//

void PostError(HINSTANCE hInst, HWND hwndOwner, LPCSTR szTitle, UINT uiErrorId, LPCSTR szValue)
{
    char szError[MAX_STR_LENGTH]  = {0};
    char szFormat[MAX_STR_LENGTH] = {0};

    WIN::LoadString(hInst, uiErrorId, szFormat, sizeof(szFormat)/sizeof(char));
    StringCchPrintf(szError, sizeof(szError), szFormat, szValue);
    MessageBox(hwndOwner, szError, szTitle, MB_OK | MB_ICONERROR);
}

/////////////////////////////////////////////////////////////////////////////
// PostError
//

void PostError(HINSTANCE hInst, HWND hwndOwner, LPCSTR szTitle, UINT uiErrorId, LPCSTR szValue, int iValue)
{
    char szError[MAX_STR_LENGTH]  = {0};
    char szFormat[MAX_STR_LENGTH] = {0};

    WIN::LoadString(hInst, uiErrorId, szFormat, sizeof(szFormat)/sizeof(char));
    StringCchPrintf(szError, sizeof(szError), szFormat, szValue, iValue);
    MessageBox(hwndOwner, szError, szTitle, MB_OK | MB_ICONERROR);
}

/////////////////////////////////////////////////////////////////////////////
// PostError
//

void PostError(HINSTANCE hInst, HWND hwndOwner, LPCSTR szTitle, UINT uiErrorId, int iValue)
{
    char szError[MAX_STR_LENGTH]  = {0};
    char szFormat[MAX_STR_LENGTH] = {0};

    WIN::LoadString(hInst, uiErrorId, szFormat, sizeof(szFormat)/sizeof(char));
    StringCchPrintf(szError, sizeof(szError), szFormat, iValue);
    MessageBox(hwndOwner, szError, szTitle, MB_OK | MB_ICONERROR);
}

/////////////////////////////////////////////////////////////////////////////
// PostFormattedError
//

void PostFormattedError(HINSTANCE hInst, HWND hwndOwner, LPCSTR szTitle, UINT uiErrorId, LPCSTR szValue)
{
    char szFormat[MAX_STR_LENGTH] = {0};
    const char* szArgs[1] = {szValue};
    LPVOID lpMessage = 0;;

    WIN::LoadString(hInst, uiErrorId, szFormat, sizeof(szFormat)/sizeof(char));
    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY, (LPVOID)szFormat, 0, 0, (LPSTR)&lpMessage, 0, (va_list*)szArgs);
    if (!lpMessage)
    {
        ReportErrorOutOfMemory(hInst, hwndOwner, szTitle);
        return;
    }
    MessageBox(hwndOwner, (LPCSTR)lpMessage, szTitle, MB_OK | MB_ICONERROR);
    LocalFree(lpMessage);
}

/////////////////////////////////////////////////////////////////////////////
// PostMsiError
//

void PostMsiError(HINSTANCE hInst, HINSTANCE hMsi, HWND hwndOwner, LPCSTR szTitle, UINT uiErrorId)
{
    switch (uiErrorId)
    {
    case ERROR_INSTALL_SUSPEND:
    case ERROR_INSTALL_USEREXIT:
    case ERROR_INSTALL_FAILURE:
    case ERROR_SUCCESS_REBOOT_REQUIRED:
    case ERROR_SUCCESS_REBOOT_INITIATED:
    case ERROR_APPHELP_BLOCK:
        break;
    case ERROR_FILE_NOT_FOUND:
    case ERROR_INVALID_NAME:
    case ERROR_PATH_NOT_FOUND:
        uiErrorId = ERROR_INSTALL_PACKAGE_OPEN_FAILED;
    default:
        {
            char szError[MAX_STR_LENGTH] = {0};
            if (0 == WIN::LoadString(hMsi, uiErrorId, szError, sizeof(szError)/sizeof(char)))
            {
                // error string does not exist, use default
                PostError(hInst, hwndOwner, szTitle, IDS_INSTALL_ERROR, uiErrorId);
            }
            else
            {
                MessageBox(hwndOwner, szError, szTitle, MB_OK | MB_ICONERROR);
            }
            return;
        }
    }
}

/////////////////////////////////////////////////////////////////////////////
// AlreadyInProgress
//
//  Attempts to create the MSISETUP mutex. Returns TRUE
//  if mutex already exists or failed to create mutex
//

bool AlreadyInProgress(HANDLE& hMutex)
{
    const char *szMutexName = "Global\\_UPWSETUP_{6589BC35-ADC6-4B65-8139-2F28EE03C281}";
    hMutex = WIN::CreateMutex(NULL /*default security descriptor*/, FALSE, szMutexName);
    if (!hMutex || ERROR_ALREADY_EXISTS == GetLastError())
        return true;

    return false;
}

/////////////////////////////////////////////////////////////////////////////
// DisplayUsage
//
void DisplayUsage (HINSTANCE hInst, HWND hwndOwner, LPCSTR szCaption)
{
    char szMessage[MAX_STR_LENGTH];

    WIN::LoadString(hInst, IDS_USAGE, szMessage, sizeof(szMessage)/sizeof(char));
    WIN::MessageBox(hwndOwner, szMessage, szCaption, MB_OK | MB_ICONINFORMATION);
}

/////////////////////////////////////////////////////////////////////////////
// ReportErrorOutOfMemory
//

void ReportErrorOutOfMemory(HINSTANCE hInst, HWND hwndOwner, LPCSTR szCaption)
{
    char szError[MAX_STR_LENGTH];

    WIN::LoadString(hInst, IDS_OUTOFMEM, szError, sizeof(szError)/sizeof(char));
    WIN::MessageBox(hwndOwner, szError, szCaption, MB_OK | MB_ICONERROR);
}


/////////////////////////////////////////////////////////////////////////////
// GetFileVersionNumber
//

DWORD GetFileVersionNumber(__in LPSTR szFilename, DWORD * pdwMSVer, DWORD * pdwLSVer)
{
    DWORD             dwResult = NOERROR;
    unsigned          uiSize;
    DWORD             dwVerInfoSize;
    DWORD             dwHandle;
    BYTE              *prgbVersionInfo = NULL;
    VS_FIXEDFILEINFO  *lpVSFixedFileInfo = NULL;

    DWORD dwMSVer = 0xffffffff;
    DWORD dwLSVer = 0xffffffff;

    dwVerInfoSize = GetFileVersionInfoSize(szFilename, &dwHandle);
    if (0 != dwVerInfoSize)
    {
        prgbVersionInfo = (LPBYTE) WIN::GlobalAlloc(GPTR, dwVerInfoSize);
        if (NULL == prgbVersionInfo)
        {
            dwResult = ERROR_NOT_ENOUGH_MEMORY;
            goto Finish;
        }

        // Read version stamping info
        if (GetFileVersionInfo(szFilename, dwHandle, dwVerInfoSize, prgbVersionInfo))
        {
            // get the value for Translation
            if (VerQueryValue(prgbVersionInfo, "\\", (LPVOID*)&lpVSFixedFileInfo, &uiSize) && (uiSize != 0))
            {
                dwMSVer = lpVSFixedFileInfo->dwFileVersionMS;
                dwLSVer = lpVSFixedFileInfo->dwFileVersionLS;
            }
        }
        else
        {
            dwResult = GetLastError();
            goto Finish;
        }
    }
    else
    {
        dwResult = GetLastError();
    }

#ifdef DEBUG
    char szVersion[255];
    StringCchPrintf(szVersion, sizeof(szVersion), "%s is version %d.%d.%d.%d\n", szFilename, HIWORD(dwMSVer), LOWORD(dwMSVer), HIWORD(dwLSVer), LOWORD(dwLSVer));
    DebugMsg("[INFO] %s", szVersion);
#endif // DEBUG

Finish:
    if (NULL != prgbVersionInfo)
        WIN::GlobalFree(prgbVersionInfo);
    if (pdwMSVer)
        *pdwMSVer = dwMSVer;
    if (pdwLSVer)
        *pdwLSVer = dwLSVer;

    return dwResult;
}

/////////////////////////////////////////////////////////////////////////////
// MimimumWindowsPlatform
//
//  Returns true if running on a platform whose major version, minor version
//  and service pack major are greater than or equal to the ones specifed
//  while making this function call
//
bool MimimumWindowsPlatform(DWORD dwMajorVersion, DWORD dwMinorVersion, WORD wServicePackMajor)
{
   OSVERSIONINFOEX osvi;
   DWORDLONG dwlConditionMask = 0;

   // Initialize the OSVERSIONINFOEX structure.
   ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
   osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
   osvi.dwMajorVersion = dwMajorVersion;
   osvi.dwMinorVersion = dwMinorVersion;
   osvi.wServicePackMajor = wServicePackMajor;
   
   // Initialize the condition mask.
   VER_SET_CONDITION(dwlConditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL);
   VER_SET_CONDITION(dwlConditionMask, VER_MINORVERSION, VER_GREATER_EQUAL);
   VER_SET_CONDITION(dwlConditionMask, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
 
   // Perform the test.
   return VerifyVersionInfo(&osvi, 
                            VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR,
                            dwlConditionMask) ? true : false;
}

/////////////////////////////////////////////////////////////////////////////
// MimimumWindowsPlatform
//
//  Returns true if running on a platform whose major version, minor version
//  and service pack major are greater than or equal to the ones specifed
//  while making this function call
//
bool ExactWindowsPlatform(DWORD dwMajorVersion, DWORD dwMinorVersion, WORD wServicePackMajor)
{
   OSVERSIONINFOEX osvi;
   DWORDLONG dwlConditionMask = 0;

   // Initialize the OSVERSIONINFOEX structure.
   ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
   osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
   osvi.dwMajorVersion = dwMajorVersion;
   osvi.dwMinorVersion = dwMinorVersion;
   osvi.wServicePackMajor = wServicePackMajor;
   
   // Initialize the condition mask.
   VER_SET_CONDITION(dwlConditionMask, VER_MAJORVERSION, VER_EQUAL);
   VER_SET_CONDITION(dwlConditionMask, VER_MINORVERSION, VER_EQUAL);
   //VER_SET_CONDITION(dwlConditionMask, VER_SERVICEPACKMAJOR, VER_EQUAL);
 
   // Perform the test.
   return VerifyVersionInfo(&osvi, 
                            VER_MAJORVERSION | VER_MINORVERSION ,
                            dwlConditionMask) ? true : false;
}

/////////////////////////////////////////////////////////////////////////////
// IsOSSupported
//
//  Returns true if running on Windows 2003, Windows XP or 
//  Windows 2000 SP3 and above. Else returns false
//
bool IsOSSupported()
{
    OSVERSIONINFO sInfoOS;
    memset((void*)&sInfoOS, 0x00, sizeof(OSVERSIONINFO));

    sInfoOS.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
    WIN::GetVersionEx(&sInfoOS);

    // We do no support any platform prior to Windows 2000
    if (5 > sInfoOS.dwMajorVersion)
        return false;

	//We don't support
	
	

	if ( ExactWindowsPlatform(5, 2, 2) && IsWow64())
	{
		return false;
	}

    // We support:
    //if(MimimumWindowsPlatform(5, 2, 0) ||   // Windows 2003 and above
    //   MimimumWindowsPlatform(5, 1, 0) ||   // Windows XP and above
    //   MimimumWindowsPlatform(5, 0, 3))     // Windows 2000 SP3 and above
    //    return true;
    //else
    //    return false;
	 if(MimimumWindowsPlatform(5, 1, 0) )
       
        return true;
    else
        return false;
}

bool IsWow64()
{
  BOOL bIsWow64 = FALSE;

  typedef BOOL (APIENTRY *LPFN_ISWOW64PROCESS)
    (HANDLE, PBOOL);

  LPFN_ISWOW64PROCESS fnIsWow64Process;

  HMODULE module = GetModuleHandle(_T("kernel32"));
  const char funcName[] = "IsWow64Process";
  fnIsWow64Process = (LPFN_ISWOW64PROCESS)
    GetProcAddress(module, funcName);

  if(NULL != fnIsWow64Process)
  {
    if (!fnIsWow64Process(GetCurrentProcess(),
                          &bIsWow64))
      return FALSE;
  }
  return bIsWow64 != FALSE;
}

//--------------------------------------------------------------------------------------
// ADVAPI32 API -- delay load
//--------------------------------------------------------------------------------------

#define ADVAPI32_DLL "advapi32.dll"

#define ADVAPI32API_CheckTokenMembership "CheckTokenMembership"
typedef BOOL (WINAPI* PFnCheckTokenMembership)(HANDLE TokenHandle, PSID SidToCheck, PBOOL IsMember);

#define ADVAPI32API_AdjustTokenPrivileges "AdjustTokenPrivileges"
typedef BOOL (WINAPI* PFnAdjustTokenPrivileges)(HANDLE TokenHandle, BOOL DisableAllPrivileges, PTOKEN_PRIVILEGES NewState, DWORD BufferLength, PTOKEN_PRIVILEGES PreviousState, PDWORD ReturnLength);

#define ADVAPI32API_OpenProcessToken "OpenProcessToken"
typedef BOOL (WINAPI* PFnOpenProcessToken)(HANDLE ProcessHandle, DWORD DesiredAccess, PHANDLE TokenHandle);

#define ADVAPI32API_LookupPrivilegeValue "LookupPrivilegeValueA"
typedef BOOL (WINAPI* PFnLookupPrivilegeValue)(LPCSTR lpSystemName, LPCSTR lpName, PLUID lpLuid);

/////////////////////////////////////////////////////////////////////////////
// IsAdmin
//
//  Returns true if current user is an administrator (or if on Win9X)
//  Returns false if current user is not an adminstrator
//
//  implemented as per KB Q118626
//

bool IsAdmin()
{
    // get the administrator sid
    PSID psidAdministrators;
    SID_IDENTIFIER_AUTHORITY siaNtAuthority = SECURITY_NT_AUTHORITY;
    if(!AllocateAndInitializeSid(&siaNtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &psidAdministrators))
        return false;

    // on NT5, use the CheckTokenMembershipAPI to correctly handle cases where
    // the Adiminstrators group might be disabled. bIsAdmin is BOOL for 
    BOOL bIsAdmin = FALSE;
    // CheckTokenMembership checks if the SID is enabled in the token. NULL for
    // the token means the token of the current thread. Disabled groups, restricted
    // SIDS, and SE_GROUP_USE_FOR_DENY_ONLY are all considered. If the function
    // returns false, ignore the result.

    HMODULE hAdvapi32 = LoadLibrary(ADVAPI32_DLL);
    if (!hAdvapi32)
        bIsAdmin = FALSE;
    else
    {
        PFnCheckTokenMembership pfnCheckTokenMembership = (PFnCheckTokenMembership)GetProcAddress(hAdvapi32, ADVAPI32API_CheckTokenMembership);
        if (!pfnCheckTokenMembership || !pfnCheckTokenMembership(NULL, psidAdministrators, &bIsAdmin))
            bIsAdmin = FALSE;
    }
    FreeLibrary(hAdvapi32);
    hAdvapi32 = 0;
    
    WIN::FreeSid(psidAdministrators);
    return bIsAdmin ? true : false;

}

/////////////////////////////////////////////////////////////////////////////
// AcquireShutdownPrivilege
//
//  Attempts to enable the SE_SHUTDOWN_NAME privilege in the process token
//
bool AcquireShutdownPrivilege()
{
    HANDLE hToken = 0;
    TOKEN_PRIVILEGES tkp;

    HMODULE hAdvapi32 = LoadLibrary(ADVAPI32_DLL);
    if (!hAdvapi32)
        return false;

    PFnOpenProcessToken pfnOpenProcessToken = (PFnOpenProcessToken)GetProcAddress(hAdvapi32, ADVAPI32API_OpenProcessToken);
    PFnLookupPrivilegeValue pfnLookupPrivilegeValue = (PFnLookupPrivilegeValue)GetProcAddress(hAdvapi32, ADVAPI32API_LookupPrivilegeValue);
    PFnAdjustTokenPrivileges pfnAdjustTokenPrivileges = (PFnAdjustTokenPrivileges)GetProcAddress(hAdvapi32, ADVAPI32API_AdjustTokenPrivileges);
    if (!pfnOpenProcessToken || !pfnLookupPrivilegeValue || !pfnAdjustTokenPrivileges)
    {
        FreeLibrary(hAdvapi32);
        return false;
    }

    // grab this process's token
    if (!pfnOpenProcessToken(WIN::GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
    {
        FreeLibrary(hAdvapi32);
        return false;
    }

    // get the LUID for the shutdown privilege
    pfnLookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &tkp.Privileges[0].Luid);

    tkp.PrivilegeCount = 1; // one privilege to set
    tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

    // get the shutdown privilege for this process
    pfnAdjustTokenPrivileges(hToken, FALSE, &tkp, 0, (PTOKEN_PRIVILEGES)NULL, 0);

    // cannot test return value of AdjustTokenPrivileges
    if (ERROR_SUCCESS != WIN::GetLastError())
    {
        FreeLibrary(hAdvapi32);
        return false;
    }

    FreeLibrary(hAdvapi32);

    return true;
}

/////////////////////////////////////////////////////////////////////////////
// SetDiagnosticMode
//
//  Turns on debug output if first char of szDebugEnvVar is set to 1
//

int g_dmDiagnosticMode = -1; // -1 until set, then DebugMsg skips fn call if 0

void SetDiagnosticMode()
{
    g_dmDiagnosticMode = 0; // disable DebugMsg to start

    char rgchBuf[64] = {0};
    if (0 != WIN::GetEnvironmentVariable(szDebugEnvVar, rgchBuf, sizeof(rgchBuf)/sizeof(char))
        && rgchBuf[0] == '1')
    {
        g_dmDiagnosticMode = 1; // enable DebugMsg output
    }
}

/////////////////////////////////////////////////////////////////////////////
// DebugMsg
//
//  Outputs debugging string to debugger if debug output is enabled
//

void DebugMsg(LPCSTR szFormat, int iArg1)
{
    if (-1 == g_dmDiagnosticMode)
    {
        SetDiagnosticMode();
    }

    if (0 == g_dmDiagnosticMode || !szFormat)
        return; // debug output is not enabled or nothing to output

    const int INT_AS_STRING_SIZE = 12;
    size_t cchFormat = lstrlen(szFormat);
    size_t cchDebug = cchFormat + INT_AS_STRING_SIZE + 1;
    char *szDebug = new char[cchDebug];
    if (!szDebug)
        return ; // out of memory

    if (FAILED(StringCchPrintf(szDebug, cchDebug, szFormat, iArg1)))
    {
        delete[] szDebug;
        return;
    }
    OutputDebugString(szDebug);
    return;
}

void DebugMsg(LPCSTR szFormat, int iArg1, int iArg2)
{
    if (-1 == g_dmDiagnosticMode)
    {
        SetDiagnosticMode();
    }

    if (0 == g_dmDiagnosticMode || !szFormat)
        return; // debug output is not enabled or nothing to output

    const int INT_AS_STRING_SIZE = 12;
    size_t cchFormat = lstrlen(szFormat);
    size_t cchDebug = cchFormat + 2 * INT_AS_STRING_SIZE + 1;
    char *szDebug = new char[cchDebug];
    if (!szDebug)
        return ; // out of memory

    if (FAILED(StringCchPrintf(szDebug, cchDebug, szFormat, iArg1, iArg2)))
    {
        delete[] szDebug;
        return;
    }
    OutputDebugString(szDebug);
    return;
}

void DebugMsg(LPCSTR szFormat, LPCSTR szArg1, LPCSTR szArg2)
{
    if (-1 == g_dmDiagnosticMode)
    {
        SetDiagnosticMode();
    }

    if (0 == g_dmDiagnosticMode || !szFormat)
        return; // debug output is not enabled or nothing to output

    size_t cchFormat = lstrlen(szFormat);
    size_t cchArg1 = (szArg1 != 0) ? lstrlen(szArg1) : 0;
    size_t cchArg2 = (szArg2 != 0) ? lstrlen(szArg2) : 0;

    if (0 == cchArg1)
    {
        OutputDebugString(szFormat);
    }
    else
    {
        size_t cchDebug = cchFormat + cchArg1 + cchArg2 + 1;
        char *szDebug = new char[cchDebug];
        if (!szDebug)
            return ; // out of memory
        if (0 == cchArg2)
        {            
            if (FAILED(StringCchPrintf(szDebug, cchDebug, szFormat, szArg1)))
            {
                delete[] szDebug;
                return;
            }
            OutputDebugString(szDebug);
        }
        else
        {
            if (FAILED(StringCchPrintf(szDebug, cchDebug, szFormat, szArg1, szArg2)))
            {
                delete[] szDebug;
                return;
            }
            OutputDebugString(szDebug);
        }
    }

    return;
}

